using System;
using System.Threading;

namespace Needle.Utils
{
    /// <summary>
    /// Helper class to prevent infinite recursion.
    /// </summary>
    /// <remarks>
    /// Usage:
    /// <code>
    /// class A {
    /// 
    ///     private Guard guard = new Guard("errormessage");
    /// 
    ///     public void PossibleRecursiveFunction(){
    ///         using (Guard g = guard.Set()){
    ///            // do stuff here that can, but should not, generate a recursive cal into this function.
    ///         }
    ///     }
    /// }
    /// class B {
    /// 
    ///     private Guard guard = new Guard("errormessage",8);
    /// 
    ///     public void PossibleRecursiveFunction(){
    ///         using (Guard g = guard.Set()){
    ///            // do stuff here that should not recurse more than 8 levels deep.
    ///         }
    ///     }
    /// }
    /// </code>
    /// </remarks>
    public class Guard : IDisposable
    {

        private int _depth;

        private readonly int _maxRecursionDepth;
        private readonly string _errmsg;

        /// <summary>
        /// initializes a new instance.
        /// </summary>
        public Guard(string errmsg)
            : this(errmsg, 1)
        { }
        /// <summary>
        /// initializes a new instance.
        /// </summary>
        public Guard(string errmsg, int maxRecursionDepth)
        {
            Ensure.GreaterThan(0, maxRecursionDepth, "maxRecursionDepth");
            _maxRecursionDepth = maxRecursionDepth;
            _errmsg = errmsg;
        }
        /// <summary></summary>
        public int Depth
        {
            get { return Interlocked.CompareExchange(ref _depth, 0, 0); }
        }

        /// <summary>
        ///  Sets the guard.
        /// </summary>
        public Guard Set()
        {
            if (Interlocked.Increment(ref _depth) > _maxRecursionDepth)
            {
                Interlocked.Decrement(ref _depth);
                throw new InvalidOperationException(_errmsg);
            }
            return this;
        }

        /// <summary>
        /// Clears the guard.
        /// </summary>
        public void Clear()
        {
            if (Interlocked.Decrement(ref _depth) < 0)
            {
                Interlocked.Increment(ref _depth);
                throw new InvalidOperationException("Unbalanced Set and Clear of Guard.");
            }
        }

        #region IDisposable Members

        /// <summary>
        /// Clears the guard, the guard is re-usable after this call!
        /// IDisposable is implemented to support the <code>using (Guard g = guard.Set()) { }</code> syntax
        /// </summary>
        void IDisposable.Dispose()
        {
            Clear();
        }

        #endregion
    }
}
